import * as THREE from "three"
import configConst from "../constant/config.const"
import CameraData from "../data/CameraData"
import cursorEnum from "../enum/cursor.enum"
import measuringPhaseEnum from "../enum/measuringPhase.enum"
import { ClickEvent, HoverEvent, UnhoverEvent } from "../event/GestureEvent"
import P from "../math/27687"
import se from "../math/69626"
import MeasurementLabelBackgroundMesh from "../mesh/MeasurementLabelBackgroundMesh"
import re from "../packages/60039"
import Matrix from "../three/Matrix"
import Vectors from "../three/Vectors"
import Comparator from "../utils/Comparator"
import funcUtil from "../utils/func.util"

var k = {
  1: "ADDED",
  2: "REMOVED",
  4: "UPDATED",
  7: "ALL",
  ADDED: 1,
  ALL: 7,
  REMOVED: 2,
  UPDATED: 4
}
export default class MeasurementLabelRenderer {
  points: any
  input: any
  mobile: any
  createPointSubscription: any
  cameraData: CameraData
  renderLayer: any
  renderOrder: any
  textRenderer: any
  getLineDetails: any
  changeCursor: any
  getPhase: any
  setSelectedLine: any
  lengthCache: any[]
  meshPool: any[]
  textContainer: THREE.Object3D
  textGeometry: THREE.PlaneGeometry
  tmpMidpoint: THREE.Vector3
  tmpCamPos: THREE.Vector3
  cameraRotation: THREE.Quaternion
  cameraProjection: Matrix
  cameraPosition: THREE.Vector3
  dataSubs: any[]
  constructor(e, t, i, s, n, r, o, a, h, l, d, c) {
    ;(this.points = e),
      (this.input = t),
      (this.mobile = i),
      (this.createPointSubscription = s),
      (this.cameraData = n),
      (this.renderLayer = r),
      (this.renderOrder = o),
      (this.textRenderer = a),
      (this.getLineDetails = h),
      (this.changeCursor = l),
      (this.getPhase = d),
      (this.setSelectedLine = c),
      (this.lengthCache = []),
      (this.meshPool = []),
      (this.textContainer = new THREE.Object3D()),
      (this.textGeometry = new THREE.PlaneBufferGeometry(1, 1)),
      (this.tmpMidpoint = new THREE.Vector3()),
      (this.tmpCamPos = new THREE.Vector3()),
      (this.cameraRotation = new THREE.Quaternion()),
      (this.cameraProjection = new Matrix()),
      (this.cameraPosition = new THREE.Vector3())
    ;(this.dataSubs = [
      this.createPointSubscription(k.REMOVED, (_e, t) => {
        const i = this.meshPool[t]
        i && (this.input.unregisterMesh(i.collider), this.textContainer.remove(i), (this.meshPool[t] = null))
        for (let e = this.points.length - 1; e < this.lengthCache.length - 1; ++e) this.lengthCache[e] = -1
      }),
      this.input.registerMeshHandler(ClickEvent, Comparator.isType(MeasurementLabelBackgroundMesh), (_e, t) => {
        var i, s
        if (this.getPhase() === measuringPhaseEnum.IDLE)
          return (
            this.setSelectedLine(
              null === (s = null === (i = null == t ? void 0 : t.parent) || void 0 === i ? void 0 : i.userData) || void 0 === s ? void 0 : s.groupIndex
            ),
            !0
          )
      })
    ]),
      this.mobile || this.dataSubs.push(...this.registerHoverHandlers())
  }
  registerHoverHandlers() {
    return [
      this.input.registerMeshHandler(HoverEvent, Comparator.isType(MeasurementLabelBackgroundMesh), () => {
        this.getPhase() === measuringPhaseEnum.IDLE && this.changeCursor(cursorEnum.FINGER)
      }),
      this.input.registerMeshHandler(UnhoverEvent, Comparator.isType(MeasurementLabelBackgroundMesh), () => {
        this.changeCursor(cursorEnum.DEFAULT)
      })
    ]
  }
  reset() {
    for (const e of this.meshPool) e && (this.input.unregisterMesh(e.collider), this.textContainer.remove(e))
    ;(this.meshPool = []), (this.lengthCache = [])
  }
  init() {}
  dispose() {
    this.textGeometry.dispose()
  }
  activate() {
    for (const e of this.dataSubs) e.renew()
  }
  deactivate() {
    for (const e of this.dataSubs) e.cancel()
    this.reset()
  }
  get container() {
    return this.textContainer
  }
  beforeRender() {
    const e = this.cameraData.pose
    this.cameraRotation.copy(e.rotation), this.cameraPosition.copy(e.position), this.cameraProjection.copy(e.projection)
    const t = se.s1(this.cameraProjection),
      i = se.Pp(this.cameraProjection),
      s = this.cameraData.height,
      n = this.cameraData.zoom(),
      r = this.cameraData.aspect()
    for (let e = 1; e < this.points.length; ++e) {
      const o = e - 1,
        a = this.points.groupFromPointIndex(e),
        h = this.getLineDetails(a, o, e)
      if (!h || a !== this.points.groupFromPointIndex(o)) {
        this.setMeshVisible(e, !1)
        continue
      }
      const l = this.points.get(o),
        d = this.points.get(e),
        c = this.setMeshVisible(e, h.labelVisible)
      if (!c) continue
      c.text !== h.labelContents && (c.text = h.labelContents)
      const u = this.cameraPosition.distanceTo(c.position)
      if ((this.updateMeshPose(c, l, d, this.cameraRotation, this.cameraPosition, u, h.rotation, i), i || t)) c.scaleFactor = re.Hn.SCALE_DISTANCE * n
      else {
        const e = P.D_(c.position, this.cameraPosition, this.cameraRotation, this.cameraProjection.asThreeMatrix4()),
          t = Math.abs(e.x)
        if (t < 1) {
          const e = se.mY(this.cameraProjection, this.cameraPosition, c.position, s, re.Hn.SCALE),
            i = (funcUtil.getMax(r, 1, 2.5) + n) * re.Hn.SCALE_ASPECT,
            o = 1 + re.Hn.SCALE_NDC - t * re.Hn.SCALE_NDC - i
          c.scaleFactor = Math.max(Math.min((1 / e) * o, 3), 0.001)
        } else c.scaleFactor = 0.001
      }
    }
  }
  setTextOpacityByPoint(e, t) {
    const i = this.meshPool[e]
    i && (i.opacity = t)
  }
  updateMeshPose(e, t, i, s, n, r, o, a) {
    this.tmpMidpoint.copy(t).add(i).multiplyScalar(0.5)
    const h = a
      ? e => this.tmpCamPos.copy(e).addScaledVector(Vectors.UP, 0.15)
      : e =>
          this.tmpCamPos
            .copy(n)
            .sub(e)
            .setLength(0.15 * r)
            .add(e)
    e.setPosition(this.tmpMidpoint, h), e.setOrientation(s, o)
  }
  render() {}
  setMeshVisible(e, t) {
    let i = this.meshPool[e]
    if (!i && t) {
      ;(i = this.textRenderer.createLabel()), i.setRenderLayer(this.renderLayer), (i.renderOrder = this.renderOrder), (i.opacity = 1), (i.visible = !1)
      const t = this.points.groupFromPointIndex(e)
      ;(i.userData.groupIndex = t), this.textContainer.add(i), this.input.registerMesh(i.collider, !1)
    }
    if (i) {
      const e = t ? 0.001 : configConst.labelConfig.LABEL_HIDDEN_OPACITY
      if (((i.visible = i.opacity > e), !i.visible)) return null
    }
    return (this.meshPool[e] = i), i
  }
}
